ποΈGitΠ―ΡΠ°ποΈ
Node / meshtastic / Meshtastic-Android / files / feature / connections / src / commonMain / kotlin / org / meshtastic / feature / connections / ui / ConnectionsScreen.kt
Displaying Raw β’ Download
feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/ConnectionsScreen.kt b8ab53e712871d076effd2defc55be720495aba5 (b8ab53e7) Text, 24.60 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.feature.connections.ui
Tff7b72import T7ee787androidx.compose.animation.AnimatedContent
Tff7b72import T7ee787androidx.compose.animation.animateContentSize
Tff7b72import T7ee787androidx.compose.animation.fadeIn
Tff7b72import T7ee787androidx.compose.animation.fadeOut
Tff7b72import T7ee787androidx.compose.animation.togetherWith
Tff7b72import T7ee787androidx.compose.foundation.layout.Arrangement
Tff7b72import T7ee787androidx.compose.foundation.layout.Box
Tff7b72import T7ee787androidx.compose.foundation.layout.Column
Tff7b72import T7ee787androidx.compose.foundation.layout.Spacer
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxSize
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxWidth
Tff7b72import T7ee787androidx.compose.foundation.layout.height
Tff7b72import T7ee787androidx.compose.foundation.layout.heightIn
Tff7b72import T7ee787androidx.compose.foundation.layout.padding
Tff7b72import T7ee787androidx.compose.foundation.layout.size
Tff7b72import T7ee787androidx.compose.material3.Card
Tff7b72import T7ee787androidx.compose.material3.ExperimentalMaterial3Api
Tff7b72import T7ee787androidx.compose.material3.Icon
Tff7b72import T7ee787androidx.compose.material3.MaterialTheme
Tff7b72import T7ee787androidx.compose.material3.Scaffold
Tff7b72import T7ee787androidx.compose.material3.Text
Tff7b72import T7ee787androidx.compose.runtime.Composable
Tff7b72import T7ee787androidx.compose.runtime.getValue
Tff7b72import T7ee787androidx.compose.runtime.mutableStateOf
Tff7b72import T7ee787androidx.compose.runtime.remember
Tff7b72import T7ee787androidx.compose.runtime.setValue
Tff7b72import T7ee787androidx.compose.ui.Alignment
Tff7b72import T7ee787androidx.compose.ui.Modifier
Tff7b72import T7ee787androidx.compose.ui.text.style.TextAlign
Tff7b72import T7ee787androidx.compose.ui.unit.dp
Tff7b72import T7ee787androidx.lifecycle.compose.LifecycleStartEffect
Tff7b72import T7ee787androidx.lifecycle.compose.collectAsStateWithLifecycle
Tff7b72import T7ee787org.jetbrains.compose.resources.stringResource
Tff7b72import T7ee787org.koin.compose.viewmodel.koinViewModel
Tff7b72import T7ee787org.meshtastic.core.model.ConnectionState
Tff7b72import T7ee787org.meshtastic.core.navigation.Route
Tff7b72import T7ee787org.meshtastic.core.navigation.SettingsRoute
Tff7b72import T7ee787org.meshtastic.core.resources.Res
Tff7b72import T7ee787org.meshtastic.core.resources.bluetooth_disabled
Tff7b72import T7ee787org.meshtastic.core.resources.connections
Tff7b72import T7ee787org.meshtastic.core.resources.no_device_selected
Tff7b72import T7ee787org.meshtastic.core.resources.open_bluetooth_settings
Tff7b72import T7ee787org.meshtastic.core.resources.open_wifi_settings
Tff7b72import T7ee787org.meshtastic.core.resources.set_your_region
Tff7b72import T7ee787org.meshtastic.core.resources.unknown_device
Tff7b72import T7ee787org.meshtastic.core.resources.wifi_unavailable
Tff7b72import T7ee787org.meshtastic.core.ui.component.AdaptiveTwoPane
Tff7b72import T7ee787org.meshtastic.core.ui.component.ListItem
Tff7b72import T7ee787org.meshtastic.core.ui.component.MainAppBar
Tff7b72import T7ee787org.meshtastic.core.ui.component.RecoveryCard
Tff7b72import T7ee787org.meshtastic.core.ui.icon.Bluetooth
Tff7b72import T7ee787org.meshtastic.core.ui.icon.Language
Tff7b72import T7ee787org.meshtastic.core.ui.icon.MeshtasticIcons
Tff7b72import T7ee787org.meshtastic.core.ui.icon.NoDevice
Tff7b72import T7ee787org.meshtastic.core.ui.util.PermissionStatus
Tff7b72import T7ee787org.meshtastic.core.ui.util.isBluetoothDisabled
Tff7b72import T7ee787org.meshtastic.core.ui.util.isWifiUnavailable
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberBluetoothPermissionState
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberLocalNetworkPermissionState
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberOpenBluetoothSettings
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberOpenWifiSettings
Tff7b72import T7ee787org.meshtastic.core.ui.viewmodel.ConnectionStatus
Tff7b72import T7ee787org.meshtastic.core.ui.viewmodel.ConnectionsViewModel
Tff7b72import T7ee787org.meshtastic.feature.connections.MOCK_DEVICE_PREFIX
Tff7b72import T7ee787org.meshtastic.feature.connections.NO_DEVICE_SELECTED
Tff7b72import T7ee787org.meshtastic.feature.connections.REPLAY_DEVICE_PREFIX
Tff7b72import T7ee787org.meshtastic.feature.connections.ScannerViewModel
Tff7b72import T7ee787org.meshtastic.feature.connections.TCP_DEVICE_PREFIX
Tff7b72import T7ee787org.meshtastic.feature.connections.model.DeviceListEntry
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.ConnectingDeviceInfo
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.CurrentlyConnectedInfo
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.DeviceList
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.TransportFilterChips
Tff7b72import T7ee787org.meshtastic.feature.settings.navigation.ConfigRoute
Tff7b72import T7ee787org.meshtastic.feature.settings.navigation.getNavRouteFrom
Tff7b72import T7ee787org.meshtastic.feature.settings.radio.RadioConfigViewModel
Tff7b72import T7ee787org.meshtastic.feature.settings.radio.component.PacketResponseStateDialog
T8b949e/**
* Fixed minimum height for the "connected device" card at the top of the Connections screen. Shared across the three UI
* states (NO_DEVICE, CONNECTING, CONNECTED_WITH_NODE) so the card never collapses or jumps size between state
* transitions. Sized to comfortably fit the CONNECTED state (battery/RSSI row + node row + disconnect button).
*/
Tff7b72private Tff7b72val Te6edf3CardMinHeight Tff7b72= T79c0ff1T79c0ff0T79c0ff0.Te6edf3dp
T8b949e/** Composable screen for managing device connections (BLE, TCP, USB). It displays connection status. */
Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalMaterial3ApiTff7b72::Te6edf3classTb4b4b4)
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffMagicNumberTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffModifierMissingTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffComposableParamOrderTa5d6ff"Tb4b4b4)
Tf0883e@Composable
Tff7b72fun Td2a8ffConnectionsScreenTb4b4b4(
Te6edf3connectionsViewModelTb4b4b4: Te6edf3ConnectionsViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3scanModelTb4b4b4: Te6edf3ScannerViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3radioConfigViewModelTb4b4b4: Te6edf3RadioConfigViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3onClickNodeChipTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onNavigateToNodeDetailsTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onConfigNavigateTb4b4b4: Tb4b4b4(Te6edf3RouteTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3radioConfigState Tff7b72by Te6edf3radioConfigViewModelTb4b4b4.Te6edf3radioConfigStateTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionProgress Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3connectionProgressTextTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionStatus Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3connectionStatusTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionState Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3ourNode Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3ourNodeForDisplayTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3regionUnset Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3regionUnsetTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3selectedDevice Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3selectedNotNullFlowTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3persistedDeviceName Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3persistedDeviceNameTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bleDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3bleDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3discoveredTcpDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3discoveredTcpDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3recentTcpDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3recentTcpDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3usbDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3usbDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3isBleScanning Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3isBleScanningTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3isNetworkScanning Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3isNetworkScanningTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bleAutoScan Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3bleAutoScanTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3networkAutoScan Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3networkAutoScanTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3showBleTransport Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3showBleTransportTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3showNetworkTransport Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3showNetworkTransportTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3showUsbTransport Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3showUsbTransportTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
T8b949e// Android 17 (API 37) gates NSD/mDNS behind ACCESS_LOCAL_NETWORK. Without this prompt the platform falls back to
T8b949e// the system "Choose a device to connect" picker on every discoverServices() call. The reactive state lets the
T8b949e// network-scan toggle request in-context and route a permanent denial to settings.
Tff7b72val Te6edf3localNetworkPermission Tff7b72= Te6edf3rememberLocalNetworkPermissionStateTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bluetoothPermission Tff7b72= Te6edf3rememberBluetoothPermissionStateTb4b4b4(Tb4b4b4)
T8b949e// Adapter-state, distinct from permission state: a permission can be granted while Bluetooth is off or the device
T8b949e// is off Wi-Fi. Detected separately so the UI can route to the adapter's settings rather than re-prompting.
Tff7b72val Te6edf3bluetoothDisabled Tff7b72= Te6edf3isBluetoothDisabledTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3wifiUnavailable Tff7b72= Te6edf3isWifiUnavailableTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3openBluetoothSettings Tff7b72= Te6edf3rememberOpenBluetoothSettingsTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3openWifiSettings Tff7b72= Te6edf3rememberOpenWifiSettingsTb4b4b4(Tb4b4b4)
T8b949e// Auto-start BLE scan when the screen is visible (lifecycle β₯ STARTED) and the user has previously opted in.
T8b949e// LifecycleStartEffect stops scanning on ON_STOP (app backgrounded) and restarts on ON_START β preventing
T8b949e// continuous background BLE radio usage that drains the battery.
Te6edf3LifecycleStartEffectTb4b4b4(Te6edf3bleAutoScanTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3bleAutoScan Tff7b72&Tff7b72& Tff7b72!Te6edf3scanModelTb4b4b4.Te6edf3isBleScanningTb4b4b4.Te6edf3valueTb4b4b4) Te6edf3scanModelTb4b4b4.Te6edf3startBleScanTb4b4b4(Tb4b4b4)
Te6edf3onStopOrDispose Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3stopBleScanTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}
Te6edf3LifecycleStartEffectTb4b4b4(Te6edf3networkAutoScanTb4b4b4, Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGrantedTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3networkAutoScan Tff7b72&Tff7b72& Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGrantedTb4b4b4) Te6edf3scanModelTb4b4b4.Te6edf3startNetworkScanTb4b4b4(Tb4b4b4)
Te6edf3onStopOrDispose Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3stopNetworkScanTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}
T8b949e/* Animate waiting for the configurations */
Tff7b72var Te6edf3isWaiting Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTb4b4b4(Tff7b72falseTb4b4b4) Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3isWaitingTb4b4b4) Tb4b4b4{
Te6edf3PacketResponseStateDialogTb4b4b4(
Te6edf3state Tff7b72= Te6edf3radioConfigStateTb4b4b4.Te6edf3responseStateTb4b4b4,
Te6edf3onDismiss Tff7b72= Tb4b4b4{
Te6edf3isWaiting Tff7b72= Tff7b72false
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3clearPacketResponseTb4b4b4(Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3onComplete Tff7b72= Tb4b4b4{
Te6edf3getNavRouteFromTb4b4b4(Te6edf3radioConfigStateTb4b4b4.Te6edf3routeTb4b4b4)Tff7b72?.Te6edf3let Tb4b4b4{ Te6edf3route Tff7b72-Tff7b72>
Te6edf3isWaiting Tff7b72= Tff7b72false
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3clearPacketResponseTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3route Tff7b72=Tff7b72= Te6edf3SettingsRouteTb4b4b4.Te6edf3LoRaTb4b4b4) Tb4b4b4{
Te6edf3onConfigNavigateTb4b4b4(Te6edf3SettingsRouteTb4b4b4.Te6edf3LoRaTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Te6edf3ScaffoldTb4b4b4(
Te6edf3topBar Tff7b72= Tb4b4b4{
Te6edf3MainAppBarTb4b4b4(
Te6edf3title Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3connectionsTb4b4b4)Tb4b4b4,
Te6edf3ourNode Tff7b72= Te6edf3ourNodeTb4b4b4,
Te6edf3showNodeChip Tff7b72= Te6edf3ourNode Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3ConnectedTb4b4b4,
Te6edf3canNavigateUp Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3onNavigateUp Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4,
Te6edf3actions Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4,
Te6edf3onClickChip Tff7b72= Tb4b4b4{ Te6edf3onClickNodeChipTb4b4b4(Tffa657itTb4b4b4.Te6edf3numTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4) Tb4b4b4{ Te6edf3paddingValues Tff7b72-Tff7b72>
Te6edf3BoxTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3ColumnTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3paddingValuesTb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3horizontal Tff7b72= T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3verticalArrangement Tff7b72= Te6edf3ArrangementTb4b4b4.Te6edf3spacedByTb4b4b4(T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3heightTb4b4b4(T79c0ff4.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3AdaptiveTwoPaneTb4b4b4(
Te6edf3first Tff7b72= Tb4b4b4{
Tff7b72val Te6edf3uiState Tff7b72=
Tff7b72when Tb4b4b4{
Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tff7b72&Tff7b72& Te6edf3ourNode Tff7b72!Tff7b72= Tff7b72null Tff7b72-Tff7b72>
Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE
Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tff7b72|Tff7b72|
Te6edf3connectionState Tff7b72=Tff7b72= Te6edf3ConnectionStateTb4b4b4.Te6edf3Connecting Tff7b72|Tff7b72|
Te6edf3selectedDevice Tff7b72!Tff7b72= Te6edf3NO_DEVICE_SELECTED Tff7b72-Tff7b72> Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTING
Tff7b72else Tff7b72-Tff7b72> Te6edf3ConnectionUiStateTb4b4b4.Te6edf3NO_DEVICE
Tb4b4b4}
T8b949e// ββ Connected Device slot ββ
T8b949e// A single Card shell hosts all three states. `animateContentSize` smooths any
T8b949e// height changes, while `heightIn(min = CardMinHeight)` reserves a stable floor so
T8b949e// the card never collapses between states.
Te6edf3CardTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3animateContentSizeTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3AnimatedContentTb4b4b4(
Te6edf3targetState Tff7b72= Te6edf3uiStateTb4b4b4,
Te6edf3label Tff7b72= Ta5d6ff"Ta5d6ffconnection_stateTa5d6ff"Tb4b4b4,
Te6edf3transitionSpec Tff7b72= Tb4b4b4{ Te6edf3fadeInTb4b4b4(Tb4b4b4) Te6edf3togetherWith Te6edf3fadeOutTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{ Te6edf3state Tff7b72-Tff7b72>
Te6edf3BoxTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3heightInTb4b4b4(Te6edf3min Tff7b72= Te6edf3CardMinHeightTb4b4b4)Tb4b4b4,
Te6edf3contentAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72when Tb4b4b4(Te6edf3stateTb4b4b4) Tb4b4b4{
Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE Tff7b72-Tff7b72>
Te6edf3ConnectedDeviceContentTb4b4b4(
Te6edf3ourNode Tff7b72= Te6edf3ourNodeTb4b4b4,
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3onNavigateToNodeDetails Tff7b72= Te6edf3onNavigateToNodeDetailsTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTING Tff7b72-Tff7b72>
Te6edf3ConnectingDeviceContentTb4b4b4(
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3persistedDeviceName Tff7b72= Te6edf3persistedDeviceNameTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3discoveredTcpDevices Tff7b72= Te6edf3discoveredTcpDevicesTb4b4b4,
Te6edf3recentTcpDevices Tff7b72= Te6edf3recentTcpDevicesTb4b4b4,
Te6edf3usbDevices Tff7b72= Te6edf3usbDevicesTb4b4b4,
Te6edf3connectionStatus Tff7b72= Te6edf3connectionStatusTb4b4b4,
Te6edf3connectionProgress Tff7b72= Te6edf3connectionProgressTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Te6edf3NoDeviceContentTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e// Region warning sits outside the animated card so it does not affect the
T8b949e// CONNECTED β CONNECTING β NO_DEVICE size transition.
Tff7b72if Tb4b4b4(
Te6edf3uiState Tff7b72=Tff7b72= Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE Tff7b72&Tff7b72&
Te6edf3regionUnset Tff7b72&Tff7b72&
Te6edf3selectedDevice Tff7b72!Tff7b72= Te6edf3MOCK_DEVICE_PREFIX Tff7b72&Tff7b72&
Te6edf3selectedDevice Tff7b72!Tff7b72= Te6edf3REPLAY_DEVICE_PREFIX
Tb4b4b4) Tb4b4b4{
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3heightTb4b4b4(T79c0ff8.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3CardTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3ListItemTb4b4b4(
Te6edf3leadingIcon Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3LanguageTb4b4b4,
Te6edf3text Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3set_your_regionTb4b4b4)Tb4b4b4,
Te6edf3onClick Tff7b72= Tb4b4b4{
Te6edf3isWaiting Tff7b72= Tff7b72true
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3setResponseStateLoadingTb4b4b4(Te6edf3ConfigRouteTb4b4b4.Te6edf3LORATb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e// Inclusive transport-visibility filter chips. Sit between the connection card and the
T8b949e// device list so users can hide entire transports they're not using.
Te6edf3TransportFilterChipsTb4b4b4(
Te6edf3showBle Tff7b72= Te6edf3showBleTransportTb4b4b4,
Te6edf3showNetwork Tff7b72= Te6edf3showNetworkTransportTb4b4b4,
Te6edf3showUsb Tff7b72= Te6edf3showUsbTransportTb4b4b4,
Te6edf3onToggleBle Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3setShowBleTransportTb4b4b4(Tff7b72!Te6edf3showBleTransportTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onToggleNetwork Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3setShowNetworkTransportTb4b4b4(Tff7b72!Te6edf3showNetworkTransportTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onToggleUsb Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3setShowUsbTransportTb4b4b4(Tff7b72!Te6edf3showUsbTransportTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
T8b949e// Adapter-off hints: shown only when the relevant permission is granted but the radio/network
T8b949e// is unavailable, so they don't overlap the permission-recovery flow on the scan toggles.
Tff7b72if Tb4b4b4(Te6edf3showBleTransport Tff7b72&Tff7b72& Te6edf3bluetoothPermissionTb4b4b4.Te6edf3isGranted Tff7b72&Tff7b72& Te6edf3bluetoothDisabledTb4b4b4) Tb4b4b4{
Te6edf3RecoveryCardTb4b4b4(
Te6edf3message Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3bluetooth_disabledTb4b4b4)Tb4b4b4,
Te6edf3actionLabel Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3open_bluetooth_settingsTb4b4b4)Tb4b4b4,
Te6edf3onAction Tff7b72= Te6edf3openBluetoothSettingsTb4b4b4,
Te6edf3actionIcon Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3BluetoothTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3showNetworkTransport Tff7b72&Tff7b72& Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGranted Tff7b72&Tff7b72& Te6edf3wifiUnavailableTb4b4b4) Tb4b4b4{
Te6edf3RecoveryCardTb4b4b4(
Te6edf3message Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3wifi_unavailableTb4b4b4)Tb4b4b4,
Te6edf3actionLabel Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3open_wifi_settingsTb4b4b4)Tb4b4b4,
Te6edf3onAction Tff7b72= Te6edf3openWifiSettingsTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3second Tff7b72= Tb4b4b4{
T8b949e// ββ Unified device list ββ
Te6edf3BoxTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3weightTb4b4b4(T79c0ff1fTb4b4b4)Tb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3DeviceListTb4b4b4(
Te6edf3connectionState Tff7b72= Te6edf3connectionStateTb4b4b4,
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3usbDevices Tff7b72= Te6edf3usbDevicesTb4b4b4,
Te6edf3discoveredTcpDevices Tff7b72= Te6edf3discoveredTcpDevicesTb4b4b4,
Te6edf3recentTcpDevices Tff7b72= Te6edf3recentTcpDevicesTb4b4b4,
Te6edf3isBleScanning Tff7b72= Te6edf3isBleScanningTb4b4b4,
Te6edf3isNetworkScanning Tff7b72= Te6edf3isNetworkScanningTb4b4b4,
Te6edf3showBleSection Tff7b72= Te6edf3showBleTransportTb4b4b4,
Te6edf3showNetworkSection Tff7b72= Te6edf3showNetworkTransportTb4b4b4,
Te6edf3showUsbSection Tff7b72= Te6edf3showUsbTransportTb4b4b4,
Te6edf3onSelectDevice Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3onSelectedTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onToggleBleScan Tff7b72= Tb4b4b4{
Tff7b72when Tb4b4b4{
T8b949e// Always allow stopping an in-progress scan.
Te6edf3isBleScanning Tff7b72-Tff7b72> Te6edf3scanModelTb4b4b4.Te6edf3toggleBleScanTb4b4b4(Tb4b4b4)
T8b949e// Granted but the radio is off β scanning can't work, so open BT settings.
Te6edf3bluetoothPermissionTb4b4b4.Te6edf3isGranted Tff7b72&Tff7b72& Te6edf3bluetoothDisabled Tff7b72-Tff7b72> Te6edf3openBluetoothSettingsTb4b4b4(Tb4b4b4)
Te6edf3bluetoothPermissionTb4b4b4.Te6edf3isGranted Tff7b72-Tff7b72> Te6edf3scanModelTb4b4b4.Te6edf3toggleBleScanTb4b4b4(Tb4b4b4)
T8b949e// Permanently denied: the system won't prompt again, so send to settings.
Te6edf3bluetoothPermissionTb4b4b4.Te6edf3status Tff7b72=Tff7b72= Te6edf3PermissionStatusTb4b4b4.Te6edf3PERMANENTLY_DENIED Tff7b72-Tff7b72>
Te6edf3bluetoothPermissionTb4b4b4.Te6edf3openAppSettingsTb4b4b4(Tb4b4b4)
T8b949e// Request in-context; once granted the user can start scanning.
Tff7b72else Tff7b72-Tff7b72> Te6edf3bluetoothPermissionTb4b4b4.Te6edf3requestTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3onToggleNetworkScan Tff7b72= Tb4b4b4{
Tff7b72when Tb4b4b4{
Te6edf3isNetworkScanning Tff7b72|Tff7b72| Te6edf3localNetworkPermissionTb4b4b4.Te6edf3isGranted Tff7b72-Tff7b72>
Te6edf3scanModelTb4b4b4.Te6edf3toggleNetworkScanTb4b4b4(Tb4b4b4)
Te6edf3localNetworkPermissionTb4b4b4.Te6edf3status Tff7b72=Tff7b72= Te6edf3PermissionStatusTb4b4b4.Te6edf3PERMANENTLY_DENIED Tff7b72-Tff7b72>
Te6edf3localNetworkPermissionTb4b4b4.Te6edf3openAppSettingsTb4b4b4(Tb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{
T8b949e// Prefer requesting the runtime grant over letting the platform fall back
T8b949e// to the system NSD picker. Persist the user's intent so that if they
T8b949e// grant after the prompt, the scan starts via the LifecycleStartEffect and
T8b949e// stays on for next session.
Te6edf3scanModelTb4b4b4.Te6edf3persistNetworkAutoScanIntentTb4b4b4(Tff7b72trueTb4b4b4)
Te6edf3localNetworkPermissionTb4b4b4.Te6edf3requestTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3onAddManualAddress Tff7b72= Tb4b4b4{ Te6edf3_Tb4b4b4, Te6edf3fullAddress Tff7b72-Tff7b72>
Tff7b72val Te6edf3displayAddress Tff7b72= Te6edf3fullAddressTb4b4b4.Te6edf3removePrefixTb4b4b4(Te6edf3TCP_DEVICE_PREFIXTb4b4b4)
Te6edf3scanModelTb4b4b4.Te6edf3addRecentAddressTb4b4b4(Te6edf3fullAddressTb4b4b4, Te6edf3displayAddressTb4b4b4)
Te6edf3scanModelTb4b4b4.Te6edf3changeDeviceAddressTb4b4b4(Te6edf3fullAddressTb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3onRemoveRecentAddress Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3removeRecentAddressTb4b4b4(Tffa657itTb4b4b4.Te6edf3fullAddressTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e/** Body for the CONNECTED state β sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffConnectedDeviceContentTb4b4b4(
Te6edf3ourNodeTb4b4b4: Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3modelTb4b4b4.Te6edf3NodeTff7b72?Tb4b4b4,
Te6edf3selectedDeviceTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3bleDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3onNavigateToNodeDetailsTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onClickDisconnectTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3ourNodeTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3node Tff7b72-Tff7b72>
Te6edf3CurrentlyConnectedInfoTb4b4b4(
Te6edf3node Tff7b72= Te6edf3nodeTb4b4b4,
Te6edf3bleDevice Tff7b72= Te6edf3bleDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4} Tff7b72as Te6edf3DeviceListEntryTb4b4b4.Te6edf3BleTff7b72?Tb4b4b4,
Te6edf3onNavigateToNodeDetails Tff7b72= Te6edf3onNavigateToNodeDetailsTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Te6edf3onClickDisconnectTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e/** Body for the CONNECTING state β sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffConnectingDeviceContentTb4b4b4(
Te6edf3selectedDeviceTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3persistedDeviceNameTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3bleDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3discoveredTcpDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3recentTcpDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3usbDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3connectionStatusTb4b4b4: Te6edf3ConnectionStatusTb4b4b4,
Te6edf3connectionProgressTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3onClickDisconnectTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3selectedEntry Tff7b72=
Te6edf3bleDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3discoveredTcpDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3recentTcpDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3usbDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
T8b949e// Use the entry name if found in scan lists, otherwise fall back to the persisted name
T8b949e// from the last successful selection, and only show "Unknown Device" as a last resort.
Tff7b72val Te6edf3name Tff7b72= Te6edf3selectedEntryTff7b72?.Te6edf3name Tff7b72?: Te6edf3persistedDeviceName Tff7b72?: Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3unknown_deviceTb4b4b4)
Tff7b72val Te6edf3address Tff7b72= Te6edf3selectedEntryTff7b72?.Te6edf3address Tff7b72?: Te6edf3selectedDevice
Te6edf3ConnectingDeviceInfoTb4b4b4(
Te6edf3deviceName Tff7b72= Te6edf3nameTb4b4b4,
Te6edf3deviceAddress Tff7b72= Te6edf3addressTb4b4b4,
Te6edf3connectionStatus Tff7b72= Te6edf3connectionStatusTb4b4b4,
Te6edf3connectionProgress Tff7b72= Te6edf3connectionProgressTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Te6edf3onClickDisconnectTb4b4b4,
Tb4b4b4)
Tb4b4b4}
T8b949e/** Body for the NO_DEVICE state β sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffNoDeviceContentTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3ColumnTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3verticalArrangement Tff7b72= Te6edf3ArrangementTb4b4b4.Te6edf3CenterTb4b4b4,
Te6edf3horizontalAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterHorizontallyTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3IconTb4b4b4(
Te6edf3imageVector Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3NoDeviceTb4b4b4,
Te6edf3contentDescription Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3sizeTb4b4b4(T79c0ff6T79c0ff4.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3tint Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3outlineVariantTb4b4b4,
Tb4b4b4)
Te6edf3TextTb4b4b4(
Te6edf3text Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3no_device_selectedTb4b4b4)Tb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3top Tff7b72= T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3style Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3typographyTb4b4b4.Te6edf3bodyLargeTb4b4b4,
Te6edf3textAlign Tff7b72= Te6edf3TextAlignTb4b4b4.Te6edf3CenterTb4b4b4,
Te6edf3color Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3outlineTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e/** Visual state for the connection screen's [AnimatedContent] transition between the three card body variants. */
Tff7b72private Tff7b72enum Tff7b72class T56d364ConnectionUiState Tb4b4b4{
T8b949e/** No device is selected. */
Te6edf3NO_DEVICETb4b4b4,
T8b949e/** A device is selected or we are actively connecting. */
Te6edf3CONNECTINGTb4b4b4,
T8b949e/** Connected with node info available. */
Te6edf3CONNECTED_WITH_NODETb4b4b4,
Tb4b4b4}
Served by rngit 1.5.4 - Generated in 0.1s